package com.ly.mp.busicen.rule.flow.filter.plugin;

import static com.ly.mp.busicen.rule.XruleStrUtils.splitParam;
import static com.ly.mp.busicen.rule.XruleStrUtils.splitXKH;

import java.beans.BeanInfo;
import java.beans.Introspector;
import java.beans.PropertyDescriptor;
import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
import java.util.stream.Stream;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;

import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.core.metadata.OrderItem;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.ly.mp.busicen.common.context.BusicenContext;
import com.ly.mp.busicen.common.context.BusicenUtils;
import com.ly.mp.busicen.common.context.ListResult;
import com.ly.mp.busicen.rule.flow.FlowSpelUtil;
import com.ly.mp.busicen.rule.flow.IDataVolume;
import com.ly.mp.busicen.rule.flow.action.ActionResult;
import com.ly.mp.busicen.rule.flow.action.IAction;
import com.ly.mp.busicen.rule.flow.action.IActionResult.Signal;
import com.ly.mp.busicen.rule.flow.action.OperationType;
import com.ly.mp.busicen.rule.flow.filter.FlowFilterBase;
import com.ly.mp.busicen.rule.flow.filter.FlowInvocation;
import com.ly.mp.busicen.rule.flow.filter.FlowResult;

/**
 * 分页查询过滤器，用于扩展selectlist类型动作节点
 * @author ly-liuweisong
 *
 */
@Component
public class PaginationFilter extends FlowFilterBase {
	
	Logger log = LoggerFactory.getLogger(PaginationFilter.class);

	@SuppressWarnings({ "unchecked", "rawtypes" })
	@Override
	public FlowResult doInvoke(FlowInvocation invocation) {
		if (invocation.action().operation().equals(OperationType.SELECTLIST)) {
			String paramStr = splitXKH(invocation.statement());
			if (!StringUtils.isEmpty(paramStr)) {
				List<String> params = Stream.of(paramStr.split(",")).filter(m->!StringUtils.isEmpty(m)).collect(Collectors.toList());
				if (params.size()==2) {
					IDataVolume data = invocation.dataVolume();
					Integer pageIndex = null;
					Integer pageSize = null;
					//分页信息获取生成
					try {
						pageIndex = FlowSpelUtil.spelGetData(invocation.action(), params.get(0), Integer.class); 
						pageSize = FlowSpelUtil.spelGetData(invocation.action(), params.get(1), Integer.class);
					} catch (Exception e) {
						log.error("流程【{}】节点【{}】获取分页信息失败",invocation.flowVolume().flow(),invocation.action().action());
						throw e;
					}
					log.info("流程【{}】节点【{}】转换为分页处理",invocation.flowVolume().flow(),invocation.action().action());
					Page<Map<String, Object>> pageInfo = new Page<Map<String,Object>>(pageIndex,pageSize);
					//包装自定义排序数据
					wrapperOrders(pageInfo);
					//备份参数
					Object extData = FlowSpelUtil.spelGetData(invocation.action(), invocation.action().extKey());
					//包装动作节点参数
					wrapper(invocation.action(), invocation.action().extKey(), pageInfo);
					FlowResult result = invocation.invoker().invoke(invocation);
					//还原参数
					FlowSpelUtil.spelSetData(invocation.action(), invocation.action().extKey(), extData);
					if (extData instanceof Map) {
						((Map) extData).remove("__pageInfo");
					}
					if (result.actionResult().signal().equals(Signal.CONTINUE)) {
						//修改结果为ListResult
						if (result.actionResult().data()!=null) {
							pageInfo.setRecords((List<Map<String, Object>>) result.actionResult().data());
						}
						ListResult<Map<String, Object>> listResult = com.ly.mp.busicen.common.context.BusicenUtils.page2ListResult(pageInfo);
						((ActionResult)result.actionResult()).setData(listResult);
						data.ext().put(invocation.action().action(), listResult);						
					}
					return result;					
				}else {
					log.info("流程【{}】节点【{}】参数个数不为2,分页处理无效",invocation.flowVolume().flow(),invocation.action().action());
				}			
			}else{
				log.info("流程【{}】节点【{}】参数为空,分页处理无效",invocation.flowVolume().flow(),invocation.action().action());
			}
		}else {
			log.info("流程【{}】节点【{}】类型不是SELECTLIST,分页处理无效",invocation.flowVolume().flow(),invocation.action().action());
		}
		
		return invocation.invoker().invoke(invocation);
	}
	
	/**
	 * 自定义排序
	 * @param pageInfo
	 */
	private void wrapperOrders(Page<Map<String, Object>> pageInfo) {
		Map<String, Object> context = BusicenContext.getContext();
		if (context!=null&&!StringUtils.isEmpty(context.get(BusicenContext.BUSICEN_QUERY_ORDERS))) {
			String busicen_query_orders = String.valueOf(context.get(BusicenContext.BUSICEN_QUERY_ORDERS));
			if (!"#".equals(busicen_query_orders)) {
				Map<String, String> ordersList = splitParam(busicen_query_orders);
				ordersList.forEach((k,v)->{
					if ("asc".equals(v)) {
						pageInfo.addOrder(OrderItem.asc(BusicenUtils.HumpToUnderline(k)));
					}
					if ("desc".equals(v)) {
						pageInfo.addOrder(OrderItem.desc(BusicenUtils.HumpToUnderline(k)));
					}
				});
			}
		}
	}
	
	@SuppressWarnings("rawtypes")
	private void wrapper(IAction action,String extKey,IPage<Map<String, Object>> pageInfo) {
		Object extData = FlowSpelUtil.spelGetData(action, extKey);// FlowFilter.getDataFromContextSpel(data, extKey);
		if (extData==null) {
			return ;
		}
		if (extData instanceof Map) {
			FlowSpelUtil.spelSetData(action, extKey, wrapperMap((Map)extData,pageInfo));
		}else {
			FlowSpelUtil.spelSetData(action, extKey, wrapperMap(entityToMap(extData),pageInfo));
		}
	}
	
	@SuppressWarnings({ "unchecked", "rawtypes" })
	private Map wrapperMap(Map data,IPage<Map<String, Object>> pageInfo) {
		data.put("__pageInfo", pageInfo);
		return data;
	}	

	public Map<String, Object> entityToMap(Object entity) {
		if (entity == null) {
			return null;
		}
		Map<String, Object> result = new HashMap<String, Object>();
		try {
			BeanInfo beanInfo = Introspector.getBeanInfo(entity.getClass());
			PropertyDescriptor[] propertyDescriptors = beanInfo.getPropertyDescriptors();
			for (PropertyDescriptor m : propertyDescriptors) {
				String name = m.getName();
				if (name.equals("callback")||name.equals("callbacks")||name.equals("class")) {
					continue;
				}
				Method method = m.getReadMethod();
				result.put(name, method.invoke(entity));
			}
		} catch (Exception e) {
		}
		return result;
	}

	@Override
	public String fileterName() {
		return "page";
	}

}
